{- This file is part of funbot.
 -
 - Written in 2015, 2017 by fr33domlover <fr33domlover@rel4tion.org>.
 -
 - ♡ Copying is an act of love. Please copy, reuse and share.
 -
 - The author(s) have dedicated all copyright and related and neighboring
 - rights to this software to the public domain worldwide. This software is
 - distributed without any warranty.
 -
 - You should have received a copy of the CC0 Public Domain Dedication along
 - with this software. If not, see
 - <http://creativecommons.org/publicdomain/zero/1.0/>.
 -}

{-# LANGUAGE OverloadedStrings #-}

module FunBot.Sources.WebListener.Gogs
    ( dispatchGogs
    )
where

import Control.Monad (when)
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
import Formatting
import FunBot.ExtEvents
import FunBot.Sources.WebListener.Util
import Network.HTTP
import Network.URI
import Network.Wai

import qualified Data.ByteString.Char8 as BC (pack)
import qualified Data.Text as T
import qualified Web.Hook.Gogs as G

makeCommit :: G.Commit -> Commit
makeCommit commit = Commit
    { commitAuthor   = G.userName $ G.commitAuthor commit
    , commitTitle    = takeLine $ G.commitMessage commit
    , commitUrl      = G.commitUrl commit
    , commitAdded    = []
    , commitModified = []
    , commitRemoved  = []
    }

makeRepo :: G.Repository -> Repository
makeRepo repo = Repository
    { repoName  = G.repoName repo
    , repoSpace = G.userUsername' $ G.repoOwner repo
    , repoHost  = fromMaybe "UNKNOWN" $ do
        uri <- parseURI $ T.unpack $ G.repoHomeUrl repo
        auth <- uriAuthority uri
        return $ T.pack $ uriRegName auth
    }

makePush :: G.Push -> ExtEvent
makePush push = GitPushEvent ProjectObject
    { poRepo = makeRepo $ G.pushRepository push
    , poObj  = Push
        { pushBranch  = refToBranch $ G.pushRef push
        , pushCommits = reverse $ map makeCommit $ G.pushCommits push
        }
    }

-- TODO GitLab module has a copy too, move to util module
shorten n t =
    if t `T.compareLength` n == GT
        then T.take n t <> "[…]"
        else t

makeIssueAction :: G.IssueAction -> T.Text
makeIssueAction G.IssueOpen        = "opened"
makeIssueAction G.IssueEdit        = "edited"
makeIssueAction G.IssueLabelUpdate = "updated labels"
makeIssueAction G.IssueClose       = "closed"

makeIssue :: G.IssueEvent -> ExtEvent
makeIssue ie = IssueEvent ProjectObject
    { poRepo = makeRepo $ G.ieRepository ie
    , poObj  = Issue
        { issueAuthor = G.userUsername' $ G.ieSender ie
        , issueId     = num
        , issueTitle  = takeLine $ G.issueTitle i
        , issueUrl    = sformat (stext % "/issues/" % int) repo num
        , issueAction = makeIssueAction $ G.ieAction ie
        }
    }
    where
    i = G.ieIssue ie
    num = G.ieNumber ie
    repo = G.repoHomeUrl $ G.ieRepository ie

showNoteTarget :: G.Issue -> T.Text
showNoteTarget i = sformat ("issue #" % int) (G.issueNumber i)

makeIssueComment :: G.IssueComment -> ExtEvent
makeIssueComment ic = NoteEvent ProjectObject
    { poRepo = makeRepo $ G.icRepository ic
    , poObj  = Note
        { noteAuthor  = G.userUsername' $ G.icSender ic
        , noteContent = shorten 200 $ takeLine $ G.commentBody $ G.icComment ic
        , noteTarget  = showNoteTarget $ G.icIssue ic
        , noteUrl     = G.commentUrl $ G.icComment ic
        }
    }

dispatchGogs _ push _pushMany request = do
    let mh = lookup "X-Gogs-Event" $ requestHeaders request
    rbody <- lazyRequestBody request
    case mh of
        Nothing -> do
            putStrLn $ "Web hook source: Gogs: No event header for:"
            print rbody
        Just h ->
            case G.parse h rbody of
                Left e -> do
                    putStrLn $ "Web hook source: Gogs: " ++ show e ++ ":"
                    print rbody
                Right (G.EventPush e) -> push $ makePush e
                Right (G.EventIssue e) -> push $ makeIssue e
                Right (G.EventIssueComment e) ->
                    case G.icAction e of
                        G.CommentCreated -> push $ makeIssueComment e
                        _                -> return ()
